package com.qs.commontools.utils.xml;

import com.alibaba.fastjson.JSON;
import com.qs.commontools.common.service.XMLService;
import org.dom4j.Document;
import org.dom4j.DocumentException;
import org.dom4j.DocumentHelper;
import org.dom4j.io.SAXReader;

import java.io.File;
import java.io.Serializable;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;

/**
 * XML工具，既是标签对象又是标签操作对象。
 * <br>注意：使用该类需要引入dom4j依赖，部分方法还需要引入fastjson依赖
 * @author qiusuo
 * @since  2021-11-13
 */
public class XMLTool implements Serializable {

    private static final long serialVersionUID = 7531121522496809292L;

    /**
     * 标签名
     */
    private String labelName;

    /**
     * 属性
     */
    private Map<String,String> labelAttributes;

    /**
     * 值
     */
    private String labelValue;

    /**
     * 子标签集合
     */
    private List<XMLTool> childs;


    // ------------------------------------------------------------------------------------------
    // 实例方法
    // ------------------------------------------------------------------------------------------

    /**
     * 获取一个xmlEntity实例。
     *
     * @return 返回一个XMLTool对象
     */
    public static XMLTool getInstance() {
        XMLTool tool = new XMLTool();
        return tool;
    }

    /**
     * 获取一个XMLTool对象的同时设置标签名。
     *
     * @param labelName 标签名
     * @return 返回一个XMLTool对象
     */
    public static XMLTool getInstance(String labelName) {
        XMLTool tool = getInstance();
        tool.setLabelName(labelName);
        return tool;
    }

    private  XMLTool(){
        init();
    }

    private void init() {
        this.labelAttributes = new HashMap<>(16);
        this.childs = new ArrayList<>();
    }

    // ------------------------------------------------------------------------------------------
    // 属性设置方法
    // ------------------------------------------------------------------------------------------

    /**
     * 获得标签名。
     *
     * @return 标签名
     */
    public String getLabelName() {
        return labelName;
    }

    /**
     * 设置标签名。
     *
     * @param labelName 设置的标签名
     */
    public void setLabelName(String labelName) {
        this.labelName = labelName;
    }

    /**
     * 新增子标签，一次添加一个。
     *
     * @param tool 子标签对象
     */
    public void setChilds(XMLTool tool) {
        this.childs.add(tool);
    }

    /**
     * 新增子标签，批量添加，注意：这里是添加不是替换。
     * <br>如果想删除某个子标签请用getChilds获取到子标签集合后进行操作
     *
     * @param tools 子标签集合
     */
    public void setChilds(List<XMLTool> tools) {
        for (XMLTool tool : tools) {
            this.childs.add(tool);
        }
    }

    /**
     * 替换子标签集合。
     *
     * @param tools 想要替换的子标签集合
     */
    public void changeChilds(List<XMLTool> tools) {
        this.childs = tools;
    }

    /**
     * 获得子标签集合。
     *
     * @return 子标签集合
     */
    public List<XMLTool> getChilds() {
        return childs;
    }

    /**
     * 设置属性的键值对,单独设置。
     *
     * @param key 属性的键
     * @param value 属性的值
     */
    public void setLabelAttributes(String key, String value) {
        this.labelAttributes.put(key,value);
    }

    /**
     * 设置属性的键值对,直接设置map集合,注意：这里是添加不是替换。
     *
     * @param attributes 属性的键
     */
    public void setLabelAttributes(Map<String,String> attributes) {
        for (Map.Entry<String,String> entry : attributes.entrySet()) {
            this.labelAttributes.put(entry.getKey(),entry.getValue());
        }
    }

    /**
     * 替换属性集合。
     *
     * @param attributes 想要替换的属性集合
     */
    public void changeLabelAttributes(Map<String,String> attributes) {
        this.labelAttributes = attributes;
    }

    /**
     * 获得当前的属性集合。
     *
     * @return 当前的属性集合
     */
    public Map<String, String> getLabelAttributes() {
        return labelAttributes;
    }

    /**
     * 获得标签的值。
     *
     * @return 值
     */
    public String getLabelValue() {
        return labelValue;
    }

    /**
     * 设置标签的值。
     *
     * @param labelValue 想要设置的值
     */
    public void setLabelValue(String labelValue) {
        this.labelValue = labelValue;
    }



    // ------------------------------------------------------------------------------------------
    // 其他方法
    // ------------------------------------------------------------------------------------------

    /**
     * 将XML形式字符串转成XML可操作对象。
     *
     * @param str XML形式字符串
     * @throws DocumentException 字符串解析成XML Document对象异常
     * @return XML可操作对象
     */
    public static XMLTool parse(String str) throws DocumentException {
        XMLService xmlService = XMLService.getInstance();
        // 将字符串转为xml文档类型
        Document document = DocumentHelper.parseText(str);
        return xmlService.parseDocument(document);
    }

    /**
     * 将XML文件转成XML可操作对象。
     *
     * @param file XML文件
     * @throws DocumentException 入参文件解析成XML Document对象异常
     * @return XML可操作对象
     */
    public static XMLTool parse(File file) throws DocumentException {
        XMLService xmlService = XMLService.getInstance();
        SAXReader reader = new SAXReader();
        // 将文件转为xml文档类型
        Document document = reader.read(file);
        // 调用解析方法
        return xmlService.parseDocument(document);
    }

    /**
     * 将XML形式字符串转成XML可操作对象的JOSN字符串形式。
     * <br>注意：使用该方法需要引入fastjson依赖
     *
     * @param str XML形式字符串
     * @throws DocumentException 字符串解析成XML Document对象异常
     * @return XML可操作对象的JSON字符串
     */
    public static String parseToJson(String str) throws DocumentException {
        return JSON.toJSONString(parse(str));
    }

    /**
     * 将XML文件转成XML可操作对象的JOSN字符串形式。
     * <br>注意：使用该方法需要引入fastjson依赖
     *
     * @param file XML文件
     * @throws DocumentException 入参文件解析成XML Document对象异常
     * @return XML可操作对象的JSON字符串
     */
    public static String parseToJson(File file) throws DocumentException {
        return JSON.toJSONString(parse(file));
    }

    @Override
    public String toString() {
        return "XMLTool{" +
                "labelName='" + labelName + '\'' +
                ", labelAttributes=" + labelAttributes +
                ", labelValue='" + labelValue + '\'' +
                ", childs=" + childs +
                '}';
    }
}

